﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;

using Perseus;
using Perseus.Data;
using Perseus.Plugins;

using Hetymine.Executioner.Commands;
using Hetymine.Executioner.Data;
using Hetymine.Executioner.Plugins;

namespace Hetymine.Executioner.Controls {
    /// <summary>
    /// Interaction logic for CommandsConfigurator.xaml
    /// </summary>
    public partial class CommandsConfigurator : UserControl, IConfigurator<Dictionary<string, List<ICommand>>> {
        private bool _IsApply = true;
        private Dictionary<string, List<ICommand>> _Commands;

        private Dictionary<string, int> _SelectedCommandIndexes;
        private string _CommandType = string.Empty;
        private ICommand _SelectedCommand;
        private CommandNameEventHandler _CommandNameChanged;
        private ScrollBarVisibilityEventHandler _ScrollBarVisibilityChanged;

        public CommandsConfigurator() {
            InitializeComponent();

            this.cCommandList.SelectionChanged += new SelectionChangedEventHandler(CommandList_SelectionChanged);
            this.cAddCommand.Click += new RoutedEventHandler(AddCommand_Click);
            this.cRemoveCommand.Click += new RoutedEventHandler(RemoveCommand_Click);
            this.cConfigurator.ScrollChanged += new ScrollChangedEventHandler(Configurator_ScrollChanged);
            this._CommandNameChanged = new CommandNameEventHandler(CommandNameChanged);
            this._ScrollBarVisibilityChanged = new ScrollBarVisibilityEventHandler(ScrollBarVisibilityChanged);
        }

        private void Configurator_ScrollChanged(object sender, ScrollChangedEventArgs e) {
            if (this.cConfigurator.ComputedVerticalScrollBarVisibility == Visibility.Visible) {
                this.cConfigurator.Padding = new Thickness(0, 0, 5, 0);
            }
            else {
                this.cConfigurator.Padding = new Thickness(0);
            }
        }

        #region IConfigurator<Dictionary<string, List<ICommand>>> Members
        public void InitializeConfig(Dictionary<string, List<ICommand>> config) {
            this._Commands = config;

            this._SelectedCommandIndexes = new Dictionary<string, int>();
        }
        public UserControl ConfigPanel { get { return this; } }
        #endregion

        private void CommandList_SelectionChanged(object sender, SelectionChangedEventArgs e) {
            if (!this._IsApply) { return; }

            int index = this.cCommandList.SelectedIndex;
            this._SelectedCommandIndexes[this._CommandType] = index;

            if (this.cConfigurator.Content != null && this.cConfigurator.Content is ILoader) {
                ((ILoader)this.cConfigurator.Content).Unload();
            }

            if (index < 0) {
                this.cEmptyCommand.Visibility = Visibility.Visible;
                this.cConfigurator.Content = null;

                // Reset scrollbar visibility to default
                this.cConfigurator.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
                this.cConfigurator.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
            }
            else {
                this.cEmptyCommand.Visibility = Visibility.Collapsed;

                // Remove previous event handlers
                if (this._SelectedCommand != null) {
                    this._SelectedCommand.Configurator.CommandNameChanged -= this._CommandNameChanged;
                    if (this._SelectedCommand.Configurator is IScrollable) {
                        ((IScrollable)this._SelectedCommand.Configurator).ScrollBarVisibilityChanged -= this._ScrollBarVisibilityChanged;
                    }
                }                

                this._SelectedCommand = this.SelectedCommand();
                this._SelectedCommand.Configurator.CommandNameChanged += this._CommandNameChanged;
                if (this._SelectedCommand.Configurator is IScrollable) {
                    var scrollable = this._SelectedCommand.Configurator as IScrollable;
                    scrollable.ScrollBarVisibilityChanged += this._ScrollBarVisibilityChanged;
                    this.cConfigurator.HorizontalScrollBarVisibility = scrollable.HorizontalScrollBarVisibility;
                    this.cConfigurator.VerticalScrollBarVisibility = scrollable.VerticalScrollBarVisibility;
                }
                else {
                    // Reset scrollbar visibility to default
                    this.cConfigurator.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
                    this.cConfigurator.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
                }
                this.cConfigurator.Content = this._SelectedCommand.Configurator.ConfigPanel;
                if (this.cConfigurator.Content is ILoader) {
                    ((ILoader)this.cConfigurator.Content).Load();
                }
            }
        }
        private void CommandNameChanged(object sender, EventArgs e) {
            // ListBox item's tag is set to the commands type
            ListBoxItem lbi = this.cCommandList.SelectedItem as ListBoxItem;                        
            string type = lbi.Tag.ToString();            
            ICommand command = this.SelectedCommand();            
            bool prefix = (this._CommandType.IsEmpty());

            TextBlock name = this.CommandListItemName(type, command, prefix);            

            ((ListBoxItem)this.cCommandList.SelectedItem).Content = name;
        }
        private ICommand SelectedCommand() {
            int index = this.cCommandList.SelectedIndex;

            if (!this._CommandType.IsEmpty()) {
                return this._Commands[this._CommandType][index];
            }
            else {
                int count = 0;
                foreach (List<ICommand> cs in this._Commands.Values) {
                    foreach (ICommand c in cs) {
                        if (count == index) {
                            return c;
                        }
                        ++count;
                    }
                }
            }

            return null;
        }

        private void AddCommand_Click(object sender, RoutedEventArgs e) {
            this.cRemoveCommand.IsEnabled = true;

            this._IsApply = false;

            // Remove all empty HotStrings
            for (int i = this._Commands[this._CommandType].Count - 1; i >= 0; i--) {
                if (this._Commands[this._CommandType][i].IsEmpty) {
                    this._Commands[this._CommandType].RemoveAt(i);
                }
            }

            this._IsApply = true;

            ICommand command = null;
            if (this._CommandType == "Trigger") {
                command = new TriggerCommand();
            }
            else {
                PluginInstance<Plugin> plugin = Executioner.PluginService[typeof(CommandPlugin), this._CommandType];
                if (plugin == null) { return; }
                CommandPlugin cp = plugin.Instance as CommandPlugin;
                if (cp.CanInitialize) {
                    command = cp.InitializeCommand();
                }
            }

            if (command != null) {
                this._Commands[this._CommandType].Add(command);
                this.cCommandList.Items.Add(this.CommandListItem(this._CommandType, command, false));
                this.cCommandList.SelectedIndex = this.cCommandList.Items.Count - 1;
                this.cCommandList.ScrollIntoView(this.cCommandList.SelectedItem);
            }
            else {
                Sounds.Error();
            }
        }
        private void RemoveCommand_Click(object sender, RoutedEventArgs e) {
            int index = this.cCommandList.SelectedIndex;
            if (index < 0) { return; }

            this._Commands[this._CommandType].RemoveAt(index);
            this.cCommandList.Items.RemoveAt(index);

            if (this.cCommandList.Items.Count == 0) {
                this.cRemoveCommand.IsEnabled = false;
            }
            else if (this.cCommandList.Items.Count <= index) {
                this.cCommandList.SelectedIndex = index - 1;
            }
            else {
                this.cCommandList.SelectedIndex = index;
            }
        }

        public void CommandTypeFilter(string type) {
            // Prevent selection change event from being triggered and updating 
            // selected command value before it's used
            this._IsApply = false;

            this._CommandType = type;
            this.cCommandList.Items.Clear();

            if (!this._Commands.ContainsKey(type)) {
                this._Commands.Add(type, new List<ICommand>());
            }

            // All command types
            if (type.IsEmpty()) {
                // Hide add and remove buttons, edit only
                this.cButtons.Visibility = Visibility.Collapsed;

                foreach (string key in this._Commands.Keys) {
                    foreach (ICommand command in this._Commands[key]) {
                        this.cCommandList.Items.Add(this.CommandListItem(key, command, true));
                    }
                }
            }
            else { // Specific command type
                if (type == "Unknown") { // Can't add unknown types
                    this.cButtons.Visibility = Visibility.Collapsed;
                }
                else {
                    this.cButtons.Visibility = Visibility.Visible;
                }

                foreach (ICommand command in this._Commands[type]) {
                    this.cCommandList.Items.Add(this.CommandListItem(type, command, false));
                }
            }

            this._IsApply = true;

            // If at least one item then enable remove command
            this.cRemoveCommand.IsEnabled = (this.cCommandList.Items.Count > 0);

            if (!this._SelectedCommandIndexes.ContainsKey(type)) {
                this._SelectedCommandIndexes.Add(type, -1);
            }
            
            // If no command is selected, then show empty command message
            if (this._SelectedCommandIndexes[type] < 0) {
                if (this.cCommandList.Items.Count > 0) {
                    this._SelectedCommandIndexes[type] = 0;
                    this.cCommandList.SelectedIndex = this._SelectedCommandIndexes[type];
                    this.cCommandList.ScrollIntoView(this.cCommandList.SelectedItem);
                }
                else {
                    this.cEmptyCommand.Visibility = Visibility.Visible;
                    this.cConfigurator.Content = null;
                }
            }
            // If a command is selected, show the commands configurator
            else if (this._SelectedCommandIndexes[type] >= this.cCommandList.Items.Count) {
                this.cEmptyCommand.Visibility = Visibility.Visible;
                this.cConfigurator.Content = null;
                this._SelectedCommandIndexes[type] = -1;
                this.cCommandList.ScrollIntoView(this.cCommandList.SelectedItem);
            }
            else {
                this.cCommandList.SelectedIndex = this._SelectedCommandIndexes[type];
                this.cCommandList.ScrollIntoView(this.cCommandList.SelectedItem);
            }                        
        }
        private ListBoxItem CommandListItem(string type, ICommand command, bool prefix) {
            ListBoxItem lbi = new ListBoxItem();

            lbi.Tag = type;

            lbi.Content = this.CommandListItemName(type, command, prefix);
            
            return lbi;
        }
        private TextBlock CommandListItemName(string type, ICommand command, bool prefix) {
            TextBlock tb = new TextBlock();
                        
            // Empty command
            if (command.IsEmpty) {
                tb.Inlines.Add(new Run("<empty " + type.ToLower() + " command>"));
                return tb;
            }
            
            // Useable command
            if (prefix) {
                Bold b = new Bold(new Run("[ "));
                b.Foreground = Brushes.DarkGray;
                tb.Inlines.Add(b);
                    
                tb.Inlines.Add(new Run(type.ToLower()));

                b = new Bold(new Run(" ]  "));
                b.Foreground = Brushes.DarkGray;
                tb.Inlines.Add(b);
            }

            if (command.Name.IsEmpty()) {
                tb.Inlines.Add(new Run("<no name set>"));
            }
            else {
                tb.Inlines.Add(new Run(command.Name));                    
            }

            if (command.CallStrings.Count > 0) {
                Bold b = new Bold(new Run("  ( "));
                b.Foreground = Brushes.DarkGray;
                tb.Inlines.Add(b);

                tb.Inlines.Add(new Italic(new Run(String.Join(", ", command.CallStrings.ToArray()))));

                b = new Bold(new Run(" )"));
                b.Foreground = Brushes.DarkGray;
                tb.Inlines.Add(b);
            }

            return tb;
        }

        private void ScrollBarVisibilityChanged(object sender, ScrollBarVisibilityEventArgs e) {
            this.cConfigurator.HorizontalScrollBarVisibility = e.HorizontalScrollBarVisibility;
            this.cConfigurator.VerticalScrollBarVisibility = e.VerticalScrollBarVisibility;
        }
    }
}
